#pragma once
#include "vk_utils.h"
#include "vk_device_context.h"

namespace vkrender
{
    struct VulkanSwapChain
    {
    public:
        static const int MAX_FRAMES_IN_FLIGHT = 2;

    public:
        std::shared_ptr<VulkanDeviceContext> context;
        VkDevice device;
        VkPhysicalDevice physicalDevice;
        
        VkFormat swapchainImageFormat;
        VkExtent2D swapchainExtent;
        VkExtent2D windowExtent;
        VkSwapchainKHR swapchain;
        VkRenderPass renderpass;

        std::vector<VkFramebuffer> swapchainFramebuffers;
        std::vector<VkImage> depthImages;
        std::vector<VkDeviceMemory> depthImageMemorys;
        std::vector<VkImageView> depthImageViews;
        std::vector<VkImage> swapchainImages;
        std::vector<VkImageView> swapchainImageViews;
        
        // synchronize objs
        std::vector<VkSemaphore> imageAvailableSemaphores;
        std::vector<VkSemaphore> renderFinishedSemaphores;
        std::vector<VkFence> inFlightFences;
        std::vector<VkFence> imagesInFlight;

        uint32_t currentFrame = 0;
    public:
        VulkanSwapChain(const std::shared_ptr<VulkanDeviceContext>& context, VkExtent2D windowExtent);
        ~VulkanSwapChain();
        void recreate(VkExtent2D windowExtent);

        VkResult acquireNextImage(uint32_t* imageIndex);
        VkResult submitCommandBuffers(VkQueue graphicsQueue, VkQueue presentQueue, const VkCommandBuffer* buffers, uint32_t* imageIndex);

        uint32_t width() const { return swapchainExtent.width; }
        uint32_t height() const { return swapchainExtent.height; }
        uint32_t imageCount() const { return (uint32_t)swapchainImages.size(); }
        VkFramebuffer getFramebuffer(uint32_t index) const { return swapchainFramebuffers[index]; }
        VkExtent2D getSwapchainExtent() const { return swapchainExtent; }

    private:
        void init(const std::shared_ptr<VulkanDeviceContext>& context);
        void createSwapchain(const std::shared_ptr<VulkanDeviceContext>& context);
        void createImageViews();
        void createRenderpass();
        void createDepthResources();
        void createFramebuffers();
        void createSyncObjects();

        VkSurfaceFormatKHR chooseSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
        VkPresentModeKHR choosePresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes);
        VkExtent2D chooseSwapchainExtent(const VkSurfaceCapabilitiesKHR& cap);

        VkFormat findDepthFormat();
    private:
        void destroyResources();
    };
}
